#include <Ethernet.h>
#include <SPI.h>
#include "Arduino.h"
#include "board.h"
#include "matrixLCD.h"
#include "getBoard.h"
#include "arduinoPins.h"
#include "inCheck.h"
#include "update_board.h"

#define PACKETSIZE 36
#define MTR_A true
#define MTR_B false

Square board[10][10]; //main chessboard
//bool status_board[8][8]; //boolean array used by MUX array 
Square *whiteKing;
Square *blackKing;
State game_state = GAMENOTSTARTED;
int NUM_PIECES_LEFT;

byte server[] = { 192,168,1,1 }; //ip Address of the server you will connect to
byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0x53, 0x36 }; //mac address of Ethernet Shield 
byte ip[] = {192, 168, 1, 2}; //ip
EthernetClient client;
int32_t this_move;
int temp, srow, scol, drow, dcol;
Move my_move;

void setup()
{
  pinMode(IN_A1, INPUT);//mux inputs
  pinMode(IN_B1, INPUT);
  pinMode(IN_C1, INPUT);
  pinMode(IN_D1, INPUT);
  pinMode(IN_E1, INPUT);
  pinMode(IN_F1, INPUT);
  pinMode(IN_G1, INPUT);
  pinMode(IN_H1, INPUT);
  pinMode(IN_A2, INPUT);
  pinMode(IN_B2, INPUT);
  pinMode(IN_C2, INPUT);
  pinMode(IN_D2, INPUT);
  pinMode(IN_E2, INPUT);
  pinMode(IN_F2, INPUT);
  pinMode(IN_G2, INPUT);
  pinMode(IN_H2, INPUT);
  
  pinMode(4, OUTPUT);//Disabling SD Card
  pinMode(10, OUTPUT);
  digitalWrite(4, HIGH);
  digitalWrite(10, LOW);

  pinMode(SEL_A, OUTPUT);//mux select lines
  pinMode(SEL_B, OUTPUT);
  
  pinMode(DIR_PIN_A, OUTPUT);
  pinMode(STEP_PIN_A, OUTPUT);
  
  pinMode(DIR_PIN_B, OUTPUT);
  pinMode(STEP_PIN_B, OUTPUT);
  
  pinMode(MAGNET, OUTPUT);
  digitalWrite(MAGNET, LOW);
  
  pinMode(BUTTON_A, INPUT);
  pinMode(BUTTON_B, INPUT);
  
  Serial.begin(9600); //enable the serial console
  initLCD();
  clearLCD();
  
  calibrate();
}

void loop()
{
  /*
  Square src = {QUEEN, BLACK, 1, 1, true, false, true};
  Square dest = {KING, WHITE, 5, 3, true, true, false};
  Move temp = {src, dest, NEWMOVE};
  send(&temp);*/
  
  int illegal_move = 0;
  
  switch(game_state) {
    
    case GAMENOTSTARTED:
      //select_color(); //For demo, arduino client is white!
      init_board();
      getBoard();
      NUM_PIECES_LEFT = 32;
      game_state = PLAYER1;
      break;
      
    case PLAYER1:
      led1(GREEN); led2(GREEN); led3(GREEN); 
      lcdPrintln("Your turn!\nHit ENTER to register move\nFor CASTLING:\nMove King\nHit CASTLE button (left arrow)\nMove Rook\nHit ENTER");
      this_move = get_move(); //calls get_board() and get_button();
      Serial.print("Move Detected: ");
      Serial.println(this_move);
      /*CASTLING*/
      if(this_move < 0) { 
          Serial.println("Move Type: Castling");
          if(!castle_handler(this_move, WHITE)) {
              //Illegal Move
              led1(RED); led2(RED); led3(RED);
              lcdPrintln("ILLEGAL MOVE!\nUndo your move & hit the ENTER Button; Then make a legal move");                
              if(get_button() == NEWMOVE) {
                  illegal_move = 1;
                  led1(GREEN); led2(GREEN); led3(GREEN);
                  break;
              }
              led1(GREEN); led2(GREEN); led3(GREEN); 
          }  

          lcdPrintln("Move Sent! Waiting for P2");
          game_state = PLAYER2;            
          break;            
      }
      
      /*EN PASSANT*/
      else if(this_move > 10000000) { 
          Serial.println("Move Type: En Passant");
          if(!enpassant_handler(this_move, WHITE)) {
              //Illegal Move
              led1(RED); led2(RED); led3(RED);
              lcdPrintln("ILLEGAL MOVE!\nUndo your move & hit the ENTER Button; Then make a legal move");                
              if(get_button() == NEWMOVE) {
                  illegal_move = 1;
                  led1(GREEN); led2(GREEN); led3(GREEN);
                  break;    
              }
          }    

          lcdPrintln("Move Sent! Waiting for P2");
          game_state = PLAYER2;       
          break; 
      }
     
     /*CAPTURE MOVES, NON-CAPTURE MOVES & PROMOTION*/
     else {
         Serial.println("Move Type: Normal Move/Promotion");
         if(!all_other_moves_handler(this_move, WHITE)) {
              //Illegal Move
              led1(RED); led2(RED); led3(RED);
              lcdPrintln("ILLEGAL MOVE!\nUndo your move & hit the ENTER Button; Then make a legal move");                
              if(get_button() == NEWMOVE) {
                  led1(GREEN); led2(GREEN); led3(GREEN); 
                  illegal_move = 1;
                  break;
              }
          }    
          
          game_state = PLAYER2;
          lcdPrintln("Move Sent! Waiting for P2");
          break; 
     }
    
    case PLAYER2:
      while(1) {
        Move player2_move;
        recv(&player2_move);
        Serial.print("\nMovetype: ");
        Serial.print(player2_move.type);
        Serial.println();
        if (player2_move.type == 0)
          break;
        lcdPrintln("Received P2 move");
        Serial.print("gonna move ");
        Serial.print(player2_move.src.col);
        Serial.print(player2_move.src.row);
        Serial.print(player2_move.dest.col);
        Serial.println(player2_move.dest.row);
        update_board(player2_move.src, player2_move.dest);
        update_after_opponent_move(player2_move.src.row, player2_move.src.col, player2_move.dest.row, player2_move.dest.col);
        game_state = PLAYER1;
        illegal_move = 0;
      }
    
    case GAMEOVER:
      break;
  }
  
}


void connect2server() {
  
  lcdPrintln("Connecting..");
  
  while (1) {
    Serial.println("Connecting.."); 
    if (client.connect(server, 8080)) {
      Serial.println("Connection Established!");
      break;
    }
    else {
      Serial.println("Could not connect! Re-trying after 1.5 seconds");
      delay(1500);
    }
  }
}

int castle_handler(int32_t this_move, Color color) {
  
    //if (!is_legal_castling(this_move, color)) //check if legal castling  
      //  return 0;
        
    int32_t king = abs(this_move)/10000;
    int32_t k_srow = king/1000;
    int32_t k_scol = (king/100)%10;
    int32_t k_drow = (king%100)/10;
    int32_t k_dcol = king%10;
    
    my_move = (Move){board[k_srow + 1][k_scol + 1], board[k_drow + 1][k_dcol + 1], CASTLING};
    update_local_board(&my_move, WHITE); //update 2-d baord array
    //send(&my_move);//send move to server
    
    return 1;
}


int enpassant_handler(int32_t this_move, Color color) {
  
    int srow = (this_move%1000000)/100000; //source row
    int scol = ((this_move%1000000)/10000)%10; //source col
    int drow = (this_move%10000)/1000; //dest row
    int dcol = ((this_move%10000)/100)%10;//dest col
    
    int cap_row = (this_move%100)/10; //captured piece row
    int cap_col = this_move%10; //captured piece col
        
    if( !check_en_passant(this_move, WHITE) || !is_legal_move(&board[srow + 1][scol + 1], &board[drow + 1][dcol + 1]) ) 
        return 0;
       
    my_move = (Move){board[srow + 1][scol + 1], board[drow + 1][dcol + 1], ENPASSANT};
    update_local_board(&my_move, WHITE); //update 2-d baord array
    //send(&my_move); //send move to server

    return 1;
}


int all_other_moves_handler(int32_t this_move, Color color) {

    int srow = this_move/1000;
    int scol = (this_move/100)%10;
    int drow = (this_move%100)/10;   
    int dcol = this_move%10;
    
    if(!is_legal_move(&board[srow + 1][scol + 1], &board[drow + 1][dcol + 1])) 
        return 0;
    
    if(srow == 1 && drow == 0 && board[srow+1][scol+1].type == PAWN) {
        board[drow + 1][dcol + 1].type = getPromotionChoice(WHITE);
        my_move = (Move){board[srow + 1][scol + 1], board[drow + 1][dcol + 1], PROMOTION};
    }
    else
        my_move = (Move){board[srow + 1][scol + 1], board[drow + 1][dcol + 1], NEWMOVE};
    
    update_local_board(&my_move, WHITE); //update 2-d baord array
    //send(&my_move); //send move to server
    
    return 1;
}
  
    

/*Send data to server */
void send(Move *move_pkt) {
  //this function doesnt do anything anymore
}

/*Recieve data from server*/
void recv(Move *move) {
  int temp;
  Serial.print("Enter move in the form \"x1 y1 x2 y2 type\"");
  
  do {
    temp = Serial.read();
  }while(temp > '9' || temp < '0');
  int x1 = temp-'0';
  do {
    temp = Serial.read();
  }while(temp > '9' || temp < '0');
  int y1 = temp-'0';
  do {
    temp = Serial.read();
  }while(temp > '9' || temp < '0');
  int x2 = temp-'0';
  do {
    temp = Serial.read();
  }while(temp > '9' || temp < '0');
  int y2 = temp-'0';
  do {
    temp = Serial.read();
  }while(temp > '9' || temp < '0');
  int type = temp-'0';
  
  Square src, dest;
  src.col = x1;
  src.row = y1;
  dest.col = x2;
  dest.row = y2;
  
  move->src = src;
  move->dest = dest;
  move->type = (Movetype)type;
}

void calibrate() {
  //Moves both motors in parallel around the same speed until they both hit the edge
  digitalWrite(DIR_PIN_A, HIGH);
  digitalWrite(DIR_PIN_B, HIGH);
  //stop when both buttons are pressed
  while((digitalRead(BUTTON_A) == HIGH) || (digitalRead(BUTTON_B) == HIGH)) {
    if(digitalRead(BUTTON_A) == HIGH) //If white motor presses button, stop it
      digitalWrite(STEP_PIN_A, HIGH);
    if(digitalRead(BUTTON_B) == HIGH) { //gray motor needs to move at less delay
      digitalWrite(STEP_PIN_B, HIGH);
      digitalWrite(STEP_PIN_B, LOW);
      digitalWrite(STEP_PIN_B, HIGH);
    }
    delayMicroseconds(120);
    digitalWrite(STEP_PIN_A, LOW);
    digitalWrite(STEP_PIN_B, LOW);
    delayMicroseconds(120);
  }

  //Go to the 0, 0 square
  rotate(1840 * -5, .6, MTR_A); //1890
  rotate(3450 * -4.5, 1, MTR_B);
  digitalWrite(MAGNET, LOW);
}
  
  
//void process_packet() {
      


